package lx;

import java.lang.reflect.Proxy;

/**
 * Created by lixiang on 2018/4/4.
 * 代理模式：
 *     静态代理
 *     动态代理
 *
 * 代理模式包含3大块：接口(Action)；代理类（captain）；目标对象（player）具体执行命令的对象
 *
 * 优势：在一个对象不适合或者不能直接引用另一个对象时我们可以通过代理模式建立一个中介类来将两个对象联系起来，并且对目标对象起到了保护作用。
 *      我们还可以通过代理模式对原有方法进行加工具有很好的扩展性。
 *
 * 局限：由于在客户端和真实对象之间增加了代理对象，可能导致访问速度变慢。代理模式实现起来比直接访问目标对象复杂，增加工作量。
 */
public class ProxyModel {
    public static void main(String[] args) {
        testStaticProxy();
        System.out.println("/////////////////////////");
        testMovingProxy();
    }

    public static void testStaticProxy(){
        PlayerA playerA = new PlayerA();
        StaticProxyModel staticProxyModel = new StaticProxyModel(playerA);
        staticProxyModel.doSomeThings();
    }

    public static void testMovingProxy(){
        MovingProxyModel<Action> actionMovingProxyModel = new MovingProxyModel<>(PlayerB.class);
        Action proxyInstance = actionMovingProxyModel.getProxyInstance();
        proxyInstance.doSomeThings();
    }
}

/**
 * 动态代理:代理类不需要实现接口，利用反射来动态的在内存中构建访问对象
 */
class MovingProxyModel<T> {

    //维护目标对象的Class
    private Class<? extends T> tClass;

    MovingProxyModel(Class<? extends T> tClass){
        this.tClass = tClass;
    }

    @SuppressWarnings("unchecked")
    public T getProxyInstance(){
        return (T) Proxy.newProxyInstance(
                tClass.getClassLoader(),
                tClass.getInterfaces(),
                (proxy,method,args) -> method.invoke(tClass.newInstance(), args)
        );
    }
}
/**
 * 静态代理：代理类也需要实现接口，然后需要注入具体的实现类
 */
class StaticProxyModel implements Action{
    private Action action;

    StaticProxyModel(Action action) {
        this.action = action;
    }

    @Override
    public void doSomeThings() {
        action.doSomeThings();
    }
}

interface Action {

    void doSomeThings();

}

class PlayerA implements Action{
    @Override
    public void doSomeThings() {
        System.out.println("playerA doSomeThings -------------------");
    }
}

class PlayerB implements Action {
    @Override
    public void doSomeThings() {
        System.out.println("playerB doSomeThings -------------------");
    }
}

